﻿using Core.Framework;
using Core.Framework.Common;
using DotNetty.Codecs.Http;
using DotNetty.Framework.BaseModel;
using DotNetty.Framework.ExtendChannelOfClient;
using DotNetty.Handlers.Timeout;
using DotNetty.Handlers.Tls;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Groups;
using DotNetty.Transport.Libuv;
using iTool.ClusterComponent;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Quartz;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;


namespace DotNetty.Framework
{
    public class DotNettyService<TMessage, THttpContext>
            where TMessage : AbstractMessage
        where THttpContext : AbstractHttpContext, new()
    {

        /// <summary>
        /// 主线程
        /// </summary>
        private IEventLoopGroup bossGroup;

        /// <summary>
        /// 任务线程
        /// </summary>
        private IEventLoopGroup workGroup;

        /// <summary>
        /// 监听对象
        /// </summary>
        private IChannel bootstrapChannel;
        private ILogger<DotNettyService<TMessage, THttpContext>> logger;

        private static IServiceProvider ServiceProvider { get; set; }

        public DotNettyService(IServiceCollection services)
        {
            services.AddTransient<AggregatedFullHttpRequestHandler<TMessage, THttpContext>>();
            services.AddTransient<TextWebSocketHandler<TMessage>>();
            services.AddTransient<BinaryWebSocketFrameHandler<TMessage>>();
            services.AddTransient<CommonChannelHandler<TMessage>>();

            // Builder
            ServiceProvider = services.BuildServiceProvider();
        }

        /// <summary>
        /// 启动
        /// </summary>
        /// <returns></returns>
        public async Task StartAsync()
        {

            this.logger = ServiceProvider.GetService<ILogger<DotNettyService<TMessage, THttpContext>>>();

            var dispatcher = new DispatcherEventLoopGroup();
            bossGroup = dispatcher;
            workGroup = new WorkerEventLoopGroup(dispatcher);

            X509Certificate2 tlsCertificate = default;

            try
            {
                // 声明一个服务端Bootstrap，每个Netty服务端程序，都由ServerBootstrap控制，通过链式的方式组装需要的参数
                // ServerBootstrap 启动NIO服务的辅助启动类,负责初始话netty服务器，并且开始监听端口的socket请求
                var bootstrap = new ServerBootstrap();

                // 设置主和工作线程组
                bootstrap.Group(bossGroup, workGroup);

                // 申明服务端通信通道为TcpServerChannel
                // 设置非阻塞,用它来建立新accept的连接,用于构造serversocketchannel的工厂类
                bootstrap.Channel<TcpServerChannel>();


                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
                    || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    bootstrap

                        // 多进程共享端口
                        .Option(ChannelOption.SoReuseport, true)
                        // 一般来说，一个端口释放后会等待两分钟之后才能再被使用，SO_REUSEADDR是让端口释放后立即就可以被再次使用。
                        .ChildOption(ChannelOption.SoReuseaddr, true);
                }


                // https://blog.csdn.net/weixin_34232617/article/details/92025178
                // Netty 配置说明
                bootstrap

                    // 设置网络IO参数等
                    .Option(ChannelOption.SoBacklog, AppSetting.iToolConfigure.AcceptQueueCount)

                    // Nagle算法是以减少数据包发送量来增进TCP/IP网络的性能
                    // https://blog.csdn.net/lclwjl/article/details/80154565
                    .Option(ChannelOption.TcpNodelay, false)

                    .Option(ChannelOption.RcvbufAllocator, new AdaptiveRecvByteBufAllocator())

                    // 设置工作线程参数 (工作管道)
                    .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
                    {

                        IChannelPipeline pipeline = channel.Pipeline;

                        if (tlsCertificate != null)
                        {
                            pipeline.AddLast(TlsHandler.Server(tlsCertificate));
                        }

                        //pipeline.AddLast(new HttpResponseEncoder());
                        //pipeline.AddLast(new HttpRequestDecoder());
                        pipeline.AddLast(new HttpServerCodec());
                        pipeline.AddLast(new HttpObjectAggregator(65536000));

                        if (AppSetting.iToolConfigure.HeartbeatTimes > 0)
                            pipeline.AddLast(new IdleStateHandler(AppSetting.iToolConfigure.HeartbeatTimes, 0, 0));

                        //自己写的业务类
                        {
                            pipeline.AddLast(ServiceProvider.GetService<BinaryWebSocketFrameHandler<TMessage>>());
                            pipeline.AddLast(ServiceProvider.GetService<TextWebSocketHandler<TMessage>>());
                            pipeline.AddLast(ServiceProvider.GetService<AggregatedFullHttpRequestHandler<TMessage, THttpContext>>());
                            pipeline.AddLast(ServiceProvider.GetService<CommonChannelHandler<TMessage>>());
                        }

                    }));


                string name = Dns.GetHostName();
                IPAddress[] ipadrlist = Dns.GetHostAddresses(name);
                foreach (IPAddress iPAddress in ipadrlist)
                {
                    if (iPAddress.AddressFamily == AddressFamily.InterNetwork)
                    {
                        Console.WriteLine($"ipa.AddressFamily: {iPAddress.AddressFamily}, iPAddress: {iPAddress}");
                        bootstrapChannel = await bootstrap.BindAsync(iPAddress, AppSetting.iToolConfigure.Port);
                    }
                }

                // 绑定监听端口
                bootstrapChannel = await bootstrap.BindAsync(IPAddress.Any, AppSetting.iToolConfigure.Port);

                _ = Executer.RunInCommandFile("ufw allow " + AppSetting.iToolConfigure.Port);

                this.CheckConnactionIsActive();

                CLog.WriteLineSuccess("\niTool>Start Connector Successfully.");

                Console.WriteLine("\niTool> Service address: "
                    + "http" + $"://127.0.0.1:{AppSetting.iToolConfigure.Port}/connector");
            }
            catch (Exception ex)
            {
                CLog.WriteLineError("\niTool>Start Connector Error.");
                CLog.WriteLineError($"iTool>{ex.Message}.");

                this.logger.LogError(ex, "DotNetty error");
                var task1 = bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
                var task2 = workGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
                await Task.WhenAll(task1, task2);
                bossGroup = default;
                workGroup = default;

                throw;
            };


        }

        /// <summary>
        /// 推出
        /// </summary>
        /// <returns></returns>
        public async Task ShutdownAsync()
        {
            Common.IsShutdowned = true;

            try
            {
                // 注销所有订阅
                foreach (var item in ExtendClientGroup.dictionaryGroup)
                {
                    await (item.Value.Key as ReceiveChannelNotifyObserver).UnsubscribeAsync();
                }

                await bootstrapChannel.CloseAsync();

                var task1 = bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
                var task2 = workGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
                await Task.WhenAll(task1, task2);
                bossGroup = default;
                workGroup = default;
                Console.WriteLine("DotNetty Shut down.");
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "DotNetty ShutdownAsync");
            }
            finally
            {

                ExtendClientInfo.dictionaryClientByChannel
                    = new ConcurrentDictionary<IChannel, string>();

                ExtendClientGroup.dictionaryGroup
                    = new ConcurrentDictionary<string, KeyValuePair<SubscribeQueueHandler<string>, IChannelGroup>>();

            }
        }


        private void CheckConnactionIsActive()
        {
            // 健康连接检查
            var job = new TimerJob();
            job.RegisterDisallowConcurrent<CheckConnactionIsActiveCheckTask>(a => a.WithIntervalInSeconds(100).RepeatForever());
        }

        public class CheckConnactionIsActiveCheckTask : ITimerJob
        {
            private IWebSocketEventTriggered<TMessage> iTriggered;

            public CheckConnactionIsActiveCheckTask()
            {
                iTriggered = ServiceProvider.GetService<IWebSocketEventTriggered<TMessage>>();
            }

            public void Execute(IJobExecutionContext context)
            {
                foreach (var item in ExtendClientInfo.dictionaryClientByChannel.Where(item => !item.Key.Open))
                {
                    iTriggered.OnClose(item.Key);
                }

                foreach (var item in ExtendClientInfo.dictionaryClientByUUID.Where(item => !item.Key.Open))
                {
                    iTriggered.OnClose(item.Key);
                }

                foreach (var item in ExtendClientGroup.dictionaryGroup.Where(item => item.Value.Value?.Count < 1))
                {
                    ExtendClientGroup.dictionaryGroup
                    .TryRemove(item.Key, out KeyValuePair<SubscribeQueueHandler<string>, IChannelGroup> keyValue);
                    (keyValue.Key as ReceiveChannelNotifyObserver).UnsubscribeAsync().ConfigureAwait(false);
                }
            }
        }

    }

    public class Common
    {
        /// <summary>
        ///  服务是否已经卸载
        /// </summary>
        public static bool IsShutdowned { get; set; }
    }

}
